package com.podium.comment.aspect;

import com.alibaba.fastjson2.JSONObject;
import com.podium.auth.entity.LoginUser;
import com.podium.auth.utils.SecurityUtils;
import com.podium.comment.annotate.OperateLog;
import com.podium.comment.utils.RequestUtils;
import com.podium.comment.utils.SequentialIdGenerator;
import com.podium.web.log.entity.SysOperateLog;
import com.podium.web.log.service.SysOperateLogService;
import com.podium.web.system.entity.SysUser;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.stream.Collectors;

/**
 * @author 邵文泽
 * @description 日志切面
 * @date 2025/5/8 15:34
 */
@Aspect
@Slf4j
@Component
public class OperateLogAspect {

    @Autowired
    private SequentialIdGenerator idGenerator;

    @Autowired
    private SysOperateLogService operateLogService;

    @Around("@annotation(operateLog)")
    public Object around(ProceedingJoinPoint joinPoint, OperateLog operateLog) throws Throwable {
        long startTime = System.currentTimeMillis();
        SysOperateLog sysOperateLog = initSysOperateLog(joinPoint, operateLog);
        Object result;
        try {
            result = joinPoint.proceed();
            sysOperateLog.setExecuteTime(System.currentTimeMillis() - startTime);
            sysOperateLog.setResponseContent(JSONObject.toJSONString(result));
            return result;
        } catch (Throwable e) {
            sysOperateLog.setExecuteTime(System.currentTimeMillis() - startTime);
            sysOperateLog.setResponseContent("Exception: " + e.getMessage());
            // 记录详细堆栈信息
            log.error("操作日志记录异常", e);
            throw e;
        }finally {
            // 异步保存日志
            operateLogService.asyncSaveLog(sysOperateLog);
        }
    }



    /**
     * 初始化日志
     *
     * @param joinPoint  切点
     * @param operateLog 日志注解
     * @return sysOperateLog
     */
    private SysOperateLog initSysOperateLog(ProceedingJoinPoint joinPoint, OperateLog operateLog) {
        SysOperateLog sysOperateLog = new SysOperateLog();
        sysOperateLog.setId(String.valueOf(idGenerator.nextId()));
        sysOperateLog.setUserId(SecurityUtils.getUserId());
        LoginUser loginUser = SecurityUtils.getLoginUser();
        if (loginUser != null && loginUser.getUser() instanceof SysUser sysUser) {
            sysOperateLog.setUserName(sysUser.getNickName());
        }
        sysOperateLog.setOperateDescribe(operateLog.title());
        sysOperateLog.setOperateMethod(joinPoint.getSignature().getName());
        sysOperateLog.setOperateType(operateLog.businessType());
        sysOperateLog.setOperateIp(RequestUtils.getIpAddress());
        String requestParams = Arrays.stream(joinPoint.getArgs())
                .map(JSONObject::toJSONString)
                .collect(Collectors.joining(";"));
        sysOperateLog.setRequestParam(requestParams);
        sysOperateLog.setCreateTime(LocalDateTime.now());
        return sysOperateLog;
    }

}
